<!DOCTYPE html>
<html lang="zh-CN">
{% load static %}
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>菜单管理树形表格</title>
    <link href="{% static '/css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static '/css/bootstrap-icons.css' %}" rel="stylesheet">
    <link href="{% static '/css/menu/menu.css' %}" rel="stylesheet">
    <link href="{% static '/css/menu/select-tree.css' %}" rel="stylesheet">
    <script src="{% static '/js/vue.global.js' %}"></script>
    <script src="{% static 'js/axios.min.js' %}"></script>
    <script src="{% static '/js/my-tree.js' %}"></script>


</head>
{% verbatim %}
<body>
<div id="app"  style="margin-top:40px;">
    <div class="clearfix" style="margin-bottom:15px;">
        <!--下拉框，input-->
        <div class="float-start" style="margin-right:10px;">
            <select @change="doSearch" class="form-select" style="width:200px;" v-model="searchForm.system_id">
                <option value="9001">后台终端系统</option>
                <option value="9002">订单系统</option>
                <option value="9003">物流系统</option>
            </select>
        </div>
        <button class="btn btn-primary float-start" @click="openAddWin"><i class="bi bi-plus"></i> 新 增</button>
    </div>
    <div style="overflow-x:auto; max-width:100%;">
        <table class="table table-striped">
            <thead>
            <tr>
                <th style="width:180px;">菜单名称</th>
                <th style="width:60px;">图标</th>
                <th style="width:50px;">排序</th>
                <th style="width:80px;">菜单类型</th>
                <th style="width:160px;">访问路径</th>
                <th style="width:60px;">状态</th>
                <th style="width:150px;">创建时间</th>
                <th style="width:150px;">操作</th>
            </tr>
            </thead>
            <tbody>
            <template v-for="item in flatMenu">
                <tr v-if="item.visible" :key="item.id">
                    <td style="text-align:left;">
                        <span v-for="n in item.level" :key="n" class="tree-indent"></span>
                        <span v-if="item.children && item.children.length" class="tree-toggle" @click="toggle(item)">
                            <span class="arrow-right" :class="{ 'arrow-down': item.expanded }"></span>
                        </span>
                        <span v-else class="tree-indent"></span>
                        {{ item.menu_name }}
                    </td>
                    <td class="icon-cell">
                        <i v-if="item.icon" :class="`bi ${item.icon}`"></i>
                    </td>
                    <td>{{ item.sort }}</td>
                    <td>
                        <span v-if="item.menu_type === 1">目录</span>
                        <span v-else-if="item.menu_type === 2">菜单</span>
                        <span v-else-if="item.menu_type === 3">按钮</span>
                        <span v-else-if="item.menu_type === 4">API</span>
                    </td>
                    <td style="text-align:left;">{{ item.path }}</td>

                    <td>
                        <span v-if="item.status === 1">正常</span>
                        <span v-else-if="item.status === 01">禁用</span>
                    </td>
                    <td>{{ item.create_date }}</td>
                    <td>
                        <button class="btn btn-primary btn-sm" style="margin-right:5px;"  @click="openEditWin(item)">修 改</button>
                        <button class="btn btn-danger btn-sm" @click="doDelete(item)">删 除</button>

                    </td>
                </tr>
            </template>
            </tbody>
        </table>
    </div>

    <!-- 新增菜单模态框 -->
    <div class="modal" tabindex="-1" :class="{ show: modalInfo.visible }"
         :style="modalInfo.visible ? 'display:block;' : 'display:none;'">
        <div class="modal-dialog" ref="modalDialog" :style="modalInfo.modalDialogStyle">
            <div class="modal-content">
                <div class="modal-header" @mousedown="startDrag">
                    <h5 class="modal-title">{{modalInfo.title}}</h5>
                    <button type="button" class="btn-close ms-auto" aria-label="Close"
                            @click="closeWindow"></button>
                </div>
                <div class="modal-body">
                    <form autocomplete="off">
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">上级菜单</label>
                            <div class="col-10">
                                <div class="input-container">
                                    <input
                                            class="form-control"
                                            v-model="dataRow.parent_name"
                                            @click="toggleTree"
                                            readonly
                                            placeholder="请选择上级菜单">
                                    <div class="tree-container" ref="treeContainer">
                                        <tree-node
                                                v-for="node in treeData"
                                                :key="node.id"
                                                :node="node"
                                                @select="onNodeSelect">
                                        </tree-node>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">菜单类型</label>
                            <div class="col-10 d-flex align-items-center">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="1" v-model="dataRow.menu_type"
                                           id="type-catalog">
                                    <label class="form-check-label" for="type-catalog">目录</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="2" v-model="dataRow.menu_type"
                                           id="type-menu">
                                    <label class="form-check-label" for="type-menu">菜单</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="3" v-model="dataRow.menu_type"
                                           id="type-btn">
                                    <label class="form-check-label" for="type-btn">按钮</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="4" v-model="dataRow.menu_type"
                                           id="type-api">
                                    <label class="form-check-label" for="type-btn">API</label>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 row" style="position:relative;">
                            <label class="col-2 col-form-label text-end">菜单图标</label>
                            <div class="col-10" style="position:relative;">
                                <input type="text" class="form-control" v-model="dataRow.icon"
                                       placeholder="请选择图标或输入图标名" readonly style="margin-bottom:8px;"
                                       @focus="modalInfo.showIconPicker = true" @click="modalInfo.showIconPicker = true" ref="iconInput">
                                <div v-if="modalInfo.showIconPicker" class="icon-picker-pop">
                    <span v-for="icon in biIcons" :key="icon" :title="icon" class="bi-icon-select"
                          :class="{selected: dataRow.icon === icon}" @click="selectIcon(icon)">
                      <i :class="'bi ' + icon"></i>
                    </span>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">菜单名称</label>
                            <div class="col-4">
                                <input type="text" class="form-control" v-model="dataRow.menu_name" placeholder="菜单管理">
                            </div>
                            <label class="col-2 col-form-label text-end">显示排序</label>
                            <div class="col-3">
                                <input type="number" class="form-control" v-model="dataRow.sort" min="0">
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">是否外链</label>
                            <div class="col-10 d-flex align-items-center">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="是" v-model="dataRow.is_link"
                                           id="islink-yes">
                                    <label class="form-check-label" for="islink-yes">是</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="否" v-model="dataRow.is_link"
                                           id="islink-no">
                                    <label class="form-check-label" for="islink-no">否</label>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">访问地址</label>
                            <div class="col-4">
                                <input type="text" class="form-control" v-model="dataRow.path" placeholder="访问路径">
                            </div>
                            <label class="col-2 col-form-label text-end">组件路径</label>
                            <div class="col-3">
                                <input type="text" class="form-control" v-model="dataRow.component"
                                       placeholder="选填">
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">菜单简称</label>
                            <div class="col-4">
                                <input type="text" class="form-control" v-model="dataRow.short_name"
                                       placeholder="简称一般两个字">
                            </div>
                            <label class="col-2 col-form-label text-end">请求方法</label>
                            <div class="col-3">
                                <select class="form-select"  v-model="dataRow.method_type">
                                    <option value="GET">GET</option>
                                    <option value="POST">POST</option>
                                    <option value="PUT">PUT</option>
                                    <option value="DELETE">DELETE</option>
                                </select>
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">权限标识</label>
                            <div class="col-10">
                                <input type="text" class="form-control" v-model="dataRow.permission"
                                       placeholder="请输入权限标识">
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">显示状态</label>
                            <div class="col-4 d-flex align-items-center">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="1" v-model="dataRow.is_visible"
                                           id="visible-yes">
                                    <label class="form-check-label" for="visible-yes">显示</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="0" v-model="dataRow.is_visible"
                                           id="visible-no">
                                    <label class="form-check-label" for="visible-no">隐藏</label>
                                </div>
                            </div>
                            <label class="col-2 col-form-label text-end">菜单状态</label>
                            <div class="col-4 d-flex align-items-center">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="1" v-model="dataRow.status"
                                           id="status-normal">
                                    <label class="form-check-label" for="status-normal">正常</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="0" v-model="dataRow.status"
                                           id="status-stop">
                                    <label class="form-check-label" for="status-stop">停用</label>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 row">
                            <label class="col-2 col-form-label text-end">是否缓存</label>
                            <div class="col-4 d-flex align-items-center">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="1" v-model="dataRow.is_cache"
                                           id="cache-no">
                                    <label class="form-check-label" for="cache-no">yes</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="radio" value="0" v-model="dataRow.is_cache"
                                           id="cache-yes">
                                    <label class="form-check-label" for="cache-yes">no</label>
                                </div>
                            </div>
                              <label class="col-2 col-form-label text-end">是否叶子</label>
                            <div class="col-3">
                                <select class="form-select"  v-model="dataRow.is_leaf">
                                    <option value="1">是</option>
                                    <option value="0">否</option>
                                </select>
                            </div>
                        </div>
                            <div class="row mb-3">
                              <label class="col-2 col-form-label text-end">备注</label>
                              <div class="col-9">
                                <textarea class="form-control modal-input-50" rows="2" v-model="dataRow.remark" placeholder="请输入内容"></textarea>
                              </div>
                            </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button v-if="modalInfo.winType==='edit'" type="button" class="btn btn-primary" @click="doUpdate">确 定</button>
                    <button v-if="modalInfo.winType==='add'" type="button" class="btn btn-primary" @click="doAdd">提 交</button>
                    <button type="button" class="btn btn-secondary" @click="closeWindow">取 消</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="/static/js/vue.global.js"></script>
<script>
    const { createApp, ref, reactive, onMounted } = Vue;

    // Bootstrap5常用图标集（可根据需要增减）
<!--    const biIcons = [-->
<!--      'bi-house', 'bi-gear', 'bi-person', 'bi-list', 'bi-lock', 'bi-briefcase', 'bi-diagram-3', 'bi-menu-app',-->
<!--      'bi-collection', 'bi-eye', 'bi-wrench', 'bi-plus', 'bi-pencil', 'bi-trash', 'bi-check', 'bi-x', 'bi-arrow-right', 'bi-arrow-down', 'bi-folder', 'bi-star', 'bi-search', 'bi-bell', 'bi-calendar', 'bi-flag', 'bi-globe', 'bi-lightning', 'bi-link', 'bi-box-arrow-up-right', 'bi-box-arrow-in-right', 'bi-box-arrow-in-left', 'bi-box-arrow-up-left', 'bi-box-arrow-down-right', 'bi-box-arrow-down-left', 'bi-box-arrow-up', 'bi-box-arrow-down', 'bi-box-arrow-left', 'bi-box-arrow-right'-->
<!--    ];-->
    const biIcons = [
    'bi-123',
    'bi-alarm',
    'bi-arrow-up',
    'bi-arrow-down',
    'bi-arrow-left',
    'bi-arrow-right',
    'bi-award',
    'bi-backspace',
    'bi-bag',
    'bi-bar-chart',
    'bi-basket',
    'bi-battery',
    'bi-bell',
    'bi-book',
    'bi-bookmark',
    'bi-bootstrap',
    'bi-box',
    'bi-briefcase',
    'bi-brightness-high',
    'bi-bug',
    'bi-building',
    'bi-bullseye',
    'bi-calculator',
    'bi-calendar',
    'bi-camera',
    'bi-capslock',
    'bi-card-checklist',
    'bi-caret-down',
    'bi-caret-left',
    'bi-caret-right',
    'bi-caret-up',
    'bi-cart',
    'bi-cash',
    'bi-cast',
    'bi-chat',
    'bi-check',
    'bi-chevron-down',
    'bi-chevron-left',
    'bi-chevron-right',
    'bi-chevron-up',
    'bi-circle',
    'bi-clipboard',
    'bi-clock',
    'bi-cloud',
    'bi-code',
    'bi-collection',
    'bi-columns',
    'bi-command',
    'bi-compass',
    'bi-cone',
    'bi-controller',
    'bi-cpu',
    'bi-credit-card',
    'bi-crop',
    'bi-cup',
    'bi-cursor',
    'bi-dash',
    'bi-diamond',
    'bi-dice',
    'bi-disc',
    'bi-display',
    'bi-door-closed',
    'bi-door-open',
    'bi-dot',
    'bi-download',
    'bi-droplet',
    'bi-earbuds',
    'bi-easel',
    'bi-egg',
    'bi-eject',
    'bi-emoji-smile',
    'bi-envelope',
    'bi-eraser',
    'bi-exclamation',
    'bi-eye',
    'bi-eyedropper',
    'bi-eyeglasses',
    'bi-facebook',
    'bi-file',
    'bi-file-earmark',
    'bi-filter',
    'bi-flag',
    'bi-flower',
    'bi-folder',
    'bi-gem',
    'bi-gift',
    'bi-globe',
    'bi-grid',
    'bi-hand-thumbs-up',
    'bi-heart',
    'bi-house',
    'bi-image',
    'bi-info-circle',
    'bi-key',
    'bi-laptop',
    'bi-lightbulb',
    'bi-link',
    'bi-list',
    'bi-lock',
    'bi-magnifying-glass',
    'bi-map',
    'bi-megaphone',
    'bi-menu-button-wide',
    'bi-moon',
    'bi-music-note'
]

    // 菜单数据全部改为bi-xxx风格
    const menuData = [
    ];
    /**模块树定义*/
    const treeData =reactive([]);

    function flattenMenu(tree, level = 0, parentExpanded = true) {
      let arr = [];
      for (const node of tree) {
        node.level = level;
        node.expanded = node.expanded ?? false;
        node.visible = parentExpanded;
        arr.push(node);
        if (node.children && node.children.length) {
          arr = arr.concat(flattenMenu(node.children, level + 1, node.expanded && node.visible));
        }
      }
      return arr;
    }

    const app=createApp({
      setup() {
        /**搜索框*/
        const searchForm=reactive({
          system_id:"9001",
        });
        const list = reactive([]);
        const flatMenu = ref([]);
        //const showAddModal = ref(false);
        //const showIconPicker = ref(false);
        // const dragging = ref(false);
        // const dragOffset = reactive({x:0, y:0});
        // const modalDialogStyle = ref({});
        const treeContainer = ref(null);
        /**modal窗口相关信息*/
        const modalInfo = reactive({
          title: '--', //标题：新增菜单、修改菜单、查看菜单
          visible: false,
          errorMsg: '',
          winType: 'add', // 新增或修改菜单edit/add
          showIconPicker: false, //是否显示图标选择器
          dragging: false, //是否正在拖动
          dragOffset: {x:0, y:0}, //拖动偏移量
          modalDialogStyle: {}, //模态框样式

        })

        /**行数据，新增和修改菜单使用*/
        const dataRow = reactive({
          id: '',
          parent_id: '',
          system_id: '',
          parent_name: '',
          menu_type: '菜单',
          icon: '',
          menu_name: '',
          sort: 0,
          is_link: 0,
          path: '',
          component: '',
          method_type: 'get',
          permission: '',
          is_visible: 1,
          status: 1,
          is_cache: 0,
          remark: ''
        });
       
        function updateFlatMenu() {
          const setVisible = (nodes, parentVisible) => {
            for (const node of nodes) {
              node.visible = parentVisible;
              if (node.children && node.children.length) {
                setVisible(node.children, parentVisible && node.expanded);
              }
            }
          };
          setVisible(list, true);
          flatMenu.value = flattenMenu(list);
          console.log("=============>flatMenu",flatMenu.value);
        }

        function toggle(item) {
          item.expanded = !item.expanded;
          updateFlatMenu();
        }

        function selectIcon(icon) {
          dataRow.icon = icon;
          modalInfo.showIconPicker = false;
        }

        function handleClickOutside(e) {
          const input = document.querySelector('[ref=iconInput]');
          const picker = document.querySelector('.icon-picker-pop');
          if (input && !input.contains(e.target) && (!picker || !picker.contains(e.target))) {
            modalInfo.showIconPicker = false;
          }
        }

        function startDrag(e) {
          if (e.button !== 0) return;
          modalInfo.dragging = true;
          const dialog = document.querySelector('.modal-dialog');
          const rect = dialog.getBoundingClientRect();
          modalInfo.dragOffset.x = e.clientX - rect.left;
          modalInfo.dragOffset.y = e.clientY - rect.top;
          document.addEventListener('mousemove', onDrag);
          document.addEventListener('mouseup', stopDrag);
        }

        function onDrag(e) {
          if (!modalInfo.dragging) return;
          let left = e.clientX - modalInfo.dragOffset.x;
          let top = e.clientY - modalInfo.dragOffset.y;
          left = Math.max(0, Math.min(window.innerWidth - 900, left));
          top = Math.max(0, Math.min(window.innerHeight - 100, top));
          modalInfo.modalDialogStyle = {
            position: 'fixed',
            left: left + 'px',
            top: top + 'px',
            margin: 0,
            zIndex: 1050,
            width: '900px',
            maxWidth: '98vw',
            minWidth: '600px',
            maxHeight: '90vh'
          };
        }

        function stopDrag() {
          modalInfo.dragging = false;
          document.removeEventListener('mousemove', onDrag);
          document.removeEventListener('mouseup', stopDrag);
        }

        /**新增菜单*/
        function openAddWin() {
          modalInfo.title = '新增菜单';
          modalInfo.visible = true;
          modalInfo.winType = 'add';
          // 初始化dataRow数据
          Object.assign(dataRow, {
            id: '',
            parent_id: '',
            system_id: searchForm.system_id,
            parent_name: '',
            menu_type: 2,
            icon: '',
            menu_name: '',
            sort: 0,
            is_link: 0,
            path: '',
            component: '',
            method_type: '',
            permission: '',
            is_visible: 1,
            status: 1,
            is_cache: 0,
            remark: ''
          })
        }

          /**打开修改窗口*/
        function openEditWin(current_row) {
            modalInfo.title = '修改菜单';
            modalInfo.visible = true;
            modalInfo.winType = 'edit'; 
            // 初始化dataRow数据
            Object.assign(dataRow, current_row);

          }

          

        
        /**关闭窗口*/
        function closeWindow() {
          modalInfo.visible = false;
          modalInfo.errorMsg = '';
          modalInfo.title="--"
        }

        /**查询菜单数据*/
        const queryList=()=>{
          axios({
            method:'get',
            url:'/demo/menu/list/',
            params:searchForm
          }).then(res=>{
            console.log("=============>data:",res.data);
            if(res.data.code===200){

              list.splice(0,list.length,...res.data.data);
              console.log("=============>list",list);
              updateFlatMenu();
            }
          })
        }
        /**查询菜单数据*/
        const doSearch=()=>{
          queryList();
        }
        /**查询模块树*/
        const queryModuleTree=()=>{
          axios({
            method:'get',
            url:'/demo/menu/select_tree/',
            params:searchForm
          }).then(res=>{
            console.log("=============>data:",res.data);
            if(res.data.code===200){
              treeData.splice(0,treeData.length,...res.data.data);
              console.log("=============>treeData",treeData);
              updateFlatMenu();
            }
          })
        }
        /**更新菜单*/
        const doUpdate=()=>{
            axios({
              method:'post',
              url:'/demo/menu/update/',
              data:dataRow
            }).then(res=>{
              console.log("=============>data:",res.data);
              if(res.data.code===200){
                queryList();
                closeWindow();
              }
            })
        }

        /**添加菜单*/
        const doAdd=()=>{
            axios({
              method:'post',
              url:'/demo/menu/add/',
              data:dataRow
            }).then(res=>{
              console.log("=============>data:",res.data);
              if(res.data.code===200){
                queryList();
                closeWindow();
              }
            })
        }
        /**删除菜单*/
        const doDelete=(row)=>{
            console.log("=============>doDelete",row);
            // 确认是否删除
            if(!confirm("确认删除吗？")){
              return;
            }
            axios({
              method:'get',
              url:'/demo/menu/delete/',
              params:{id:row.id}
            }).then(res=>{
              console.log("=============>data:",res.data);
              if(res.data.code===200){
                queryList();
                closeWindow();
              }
            })
        }




       const toggleTree = () => {
           queryModuleTree();
           console.log('===Toggle Tree========');
           const container = treeContainer.value;
           container.style.display = container.style.display === 'block' ? 'none' : 'block';
        };

        const onNodeSelect = (node) => {
           console.log('===Selected Node:', node);
           dataRow.parent_name = node.label;
           dataRow.parent_id = node.id;
           treeContainer.value.style.display = 'none';
         };

        onMounted(() => {
          queryList();
          updateFlatMenu();
        });

        return {
          list,
          flatMenu,
          //showAddModal,
         // showIconPicker,
          biIcons,
         // dragging,
         // dragOffset,
        //  modalDialogStyle,
          toggle,
          updateFlatMenu,
          selectIcon,
          handleClickOutside,
          onDrag,
          stopDrag,
          openAddWin,
          queryList,
          searchForm,
          startDrag,
          doSearch,
          treeData, treeContainer, toggleTree, onNodeSelect,
          dataRow,
          modalInfo,
          closeWindow,
          openEditWin,
          doUpdate,
          doAdd,
          doDelete,

        };
      }
    });
    app.component('tree-node', TreeNode);
    app.mount('#app');
</script>
</body>
{% endverbatim %} <!-- 添加此行 -->
</html>